package com.leco.demo.spring.processor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * @author greg
 * @version 2022/10/23
 * 模拟ComponentScan处理器
 **/
public class ComponentScanProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        try {
            AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
            CachingMetadataReaderFactory cachingMetadataReaderFactory = new CachingMetadataReaderFactory();

            for (String candidateBeanName : registry.getBeanDefinitionNames()) {
                BeanDefinition beanDefinition = registry.getBeanDefinition(candidateBeanName);
                if (beanDefinition instanceof AbstractBeanDefinition) {
                    AbstractBeanDefinition candidate = (AbstractBeanDefinition) beanDefinition;
                    ComponentScan componentScan = AnnotationUtils.findAnnotation(candidate.getBeanClass(), ComponentScan.class);
                    if (componentScan != null) {
                        for (String p : componentScan.basePackages()) {
                            //            System.out.println(p);
                            String path = "classpath*:" + p.replace(".", "/") + "/**/*.class";
                            //            System.out.println(path);

                            Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
                            for (Resource resource : resources) {
                                //                System.out.println(resource.getFile());
                                MetadataReader metadataReader = cachingMetadataReaderFactory.getMetadataReader(resource);
                                //                System.out.println(metadataReader.getClassMetadata().getClassName());
                                AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
                                boolean hasComponent = annotationMetadata.hasAnnotation(Component.class.getName());
                                boolean hasMetaComponent = annotationMetadata.hasMetaAnnotation(Component.class.getName());
                                //                System.out.println(hasComponent);
                                //                System.out.println(hasMetaComponent);
                                if (hasComponent || hasMetaComponent) {
                                    AbstractBeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(metadataReader.getClassMetadata().getClassName()).getBeanDefinition();
                                    String beanName = generator.generateBeanName(bd, registry);
                                    registry.registerBeanDefinition(beanName, bd);
                                }

                            }
                        }

                    }
                }
            }


        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
